import { Asset, Component, Constructor, Prefab, SpriteFrame } from "cc";
import { getUIClassBUrl } from "../ui/UIManager";
import { DestroyHook } from "./DestroyHook";

/**
 * 资源加载管理
 */
export class ResLoader {
    /** 待加载的资源 */
    private toLoadAssets: { type: Constructor<Asset> | null, bUrl: IBundleUrl }[] = []
    /** 已加载的资源 */
    private loadedAssets: Asset[] = []
    /** 标记已调用释放资源接口 */
    private released: boolean = false;

    /**
     * 添加待加载资源（通用）
     * @param bUrl 资源路径
     * @param type 资源类型
     */
    addAsset(bUrl: IBundleUrl, type: Constructor<Asset> | null) {
        this.toLoadAssets.push({
            type: type,
            bUrl: bUrl
        });
        return this;
    }

    /**
     * 添加待加载精灵资源
     * @param bUrl 资源路径
     */
    addSpriteFrame(bUrl: IBundleUrl) { 
        return this.addAsset(bUrl, SpriteFrame);
    }

    /**
     * 添加待加载UI预制体资源（并调用组件R函数加载其他额外资源）
     * @param uiClass UI类
     */
    addUI<UI extends Component>(uiClass: Constructor<UI>) {
        this.addAsset(getUIClassBUrl(uiClass), Prefab);
        if (typeof uiClass['R'] === "function") {
            (uiClass['R'] as Function).call(uiClass, this);
        }
        return this;
    }

    /**
     * 加载所有资源
     */
    async load() {
        let toLoadPromises = this.toLoadAssets.map(toLoad => game.res.loadAssetAsync(toLoad.bUrl, toLoad.type));
        let toLoadResults = await Promise.all(toLoadPromises);
        toLoadResults.forEach(asset => {
            // 资源加引用计数
            asset.addRef();
            if (!this.released) {
                this.loadedAssets.push(asset);
            } else {
                asset.decRef(true);
            }
        })
    }

    
    /**
     * 为组件添加自动释放资源引用计数的功能组件
     * @param comp 组件
     */
    addAutoRelease(comp: Component) {
        comp.node.addComponent(DestroyHook).addHook(() => {
            this.releaseResRef()
        });
        return this;
    }
    
    /**
     * 释放已加载资源的引用计数
     */
    releaseResRef() {
        if (this.released){
            return;
        }
        this.released = true;
        while (this.loadedAssets.length) {
            this.loadedAssets.pop().decRef(true);
        }
    }
}